#![windows_subsystem = "windows"]

extern crate native_windows_derive as nwd;
extern crate native_windows_gui as nwg;

use anyhow::bail;
use nwd::NwgUi;
use nwg::{NativeUi, NoticeSender};
use serde::{Deserialize, Serialize};
use std::{
    cell::RefCell,
    sync::mpsc::{channel, Receiver, Sender},
    thread,
    time::Duration,
};
use tungstenite::{connect, Message};
use url::Url;
/// The dialog UI
#[derive(Default, NwgUi)]
pub struct UpdateNickNameDialog {
    data: RefCell<Option<String>>,

    #[nwg_control(size: (300, 145), position: (650, 300), title: "修改昵称", flags: "WINDOW|VISIBLE")]
    #[nwg_events( OnWindowClose: [UpdateNickNameDialog::close] )]
    window: nwg::Window,

    #[nwg_resource(family: "Arial", size: 20)]
    arial1: nwg::Font,

    #[nwg_control(text: "请输入昵称：",size:(280,35),position:(10,10), font: Some(&data.arial1))]
    lbl_code: nwg::Label,

    #[nwg_control(text: "", size: (280, 35), position: (10, 50), focus: true)]
    edt_nickname: nwg::TextInput,

    #[nwg_control(text: "确定", position: (10, 90), size: (130, 50))]
    #[nwg_events( OnButtonClick: [UpdateNickNameDialog::choose(SELF, CTRL)] )]
    choice_yes: nwg::Button,

    #[nwg_control(text: "取消", position: (160, 90), size: (130, 50), focus: true)]
    #[nwg_events( OnButtonClick: [UpdateNickNameDialog::choose(SELF, CTRL)] )]
    choice_no: nwg::Button,
}

impl UpdateNickNameDialog {
    /// Create the dialog UI on a new thread. The dialog result will be returned by the thread handle.
    /// To alert the main GUI that the dialog completed, this function takes a notice sender object.
    fn popup(sender: nwg::NoticeSender) -> thread::JoinHandle<String> {
        thread::spawn(move || {
            // Create the UI just like in the main function
            let app =
                UpdateNickNameDialog::build_ui(Default::default()).expect("Failed to build UI");
            nwg::dispatch_thread_events();

            // Notice the main thread that the dialog completed
            sender.notice();

            // Return the dialog data
            app.data.take().unwrap_or("".to_owned())
        })
    }

    fn close(&self) {
        nwg::stop_thread_dispatch();
    }

    fn choose(&self, btn: &nwg::Button) {
        let mut data = self.data.borrow_mut();
        if btn == &self.choice_no {
            *data = Some("".to_string());
        } else if btn == &self.choice_yes {
            // 获取昵称，进行更新
            let nickname = self.edt_nickname.text();
            *data = Some(nickname);
        }

        self.window.close();
    }
}

#[derive(Default, NwgUi)]
pub struct MainApp {
    loaded_image: RefCell<Option<nwg::Bitmap>>,
    #[nwg_control(size: (560, 480), position: (300, 300), title: "Rabbit 客户端", flags: "MINIMIZE_BOX|WINDOW|VISIBLE")]
    #[nwg_events( OnWindowClose: [MainApp::exit],OnInit:[MainApp::load_data] )]
    window: nwg::Window,

    #[nwg_resource]
    decoder: nwg::ImageDecoder,

    #[nwg_resource(family: "Arial", size: 16)]
    arial16: nwg::Font,
    #[nwg_resource(family: "Arial", size: 24)]
    font_title: nwg::Font,

    #[nwg_layout(parent: window)]
    layout: nwg::GridLayout,

    #[nwg_control(text: "加入聊天室，述说你的故事", font: Some(&data.font_title),h_align:HTextAlign::Center)]
    #[nwg_layout_item(layout: layout, col: 0,row: 0, col_span: 6, row_span:1)]
    lbl_desc1: nwg::Label,

    #[nwg_control(text: "扫描右侧二维码获取验证码", font: Some(&data.arial16),h_align:HTextAlign::Right)]
    #[nwg_layout_item(layout: layout, col: 0,row: 1, col_span: 6, row_span:1)]
    lbl_desc2: nwg::Label,

    #[nwg_control(size:(430,430))]
    #[nwg_layout_item(layout: layout, col: 6, row: 0, col_span: 3, row_span: 3)]
    img_gzh: nwg::ImageFrame,

    #[nwg_control(size: (480, 480), position: (10, 10), readonly:true, flags: "VISIBLE|AUTOVSCROLL")]
    #[nwg_layout_item(layout: layout, col: 0, col_span: 6, row: 2, row_span: 7)]
    msg_box: nwg::TextBox,

    #[nwg_control(text: "请输入验证码：", font: Some(&data.arial16))]
    #[nwg_layout_item(layout: layout, col: 6,col_span:3, row: 3)]
    lbl_code: nwg::Label,

    #[nwg_control(text: "", size: (280, 35), position: (10, 10), focus: true)]
    #[nwg_layout_item(layout: layout, col: 6,col_span:3, row: 4)]
    edt_code: nwg::TextInput,

    #[nwg_control(text: "登录", size: (280, 70), position: (10, 50))]
    #[nwg_layout_item(layout: layout, col: 6,col_span:3, row: 5)]
    #[nwg_events( OnButtonClick: [MainApp::login] )]
    btn_login: nwg::Button,

    #[nwg_control(text: "修改昵称", size: (280, 70), position: (10, 50), enabled:false)]
    #[nwg_layout_item(layout: layout, col: 6,col_span:3, row: 6)]
    #[nwg_events( OnButtonClick: [MainApp::update_nickname] )]
    btn_nickname: nwg::Button,

    // #[nwg_control(collection: vec!["默认房间", "小鸭子", "天上地下", "好几次地方","好几次地方啊"], selected_index: Some(0), font: Some(&data.arial16),enabled:false)]
    // #[nwg_layout_item(layout: layout, col: 6,col_span:3, row: 7)]
    // #[nwg_events( OnComboxBoxSelection: [MainApp::switch_room] )]
    // cbx_room: nwg::ComboBox<&'static str>,
    #[nwg_control(text: "", size: (280, 35), position: (10, 10),readonly:true)]
    #[nwg_layout_item(layout: layout, col: 0,row: 9,col_span:6,row_span: 1 )]
    edt_msg: nwg::TextInput,

    #[nwg_control(text: "发送", size: (280, 70), position: (10, 50), enabled:false)]
    #[nwg_layout_item(layout: layout, col: 6,row: 9,col_span:3,row_span: 1)]
    #[nwg_events( OnButtonClick: [MainApp::send_msg] )]
    btn_send: nwg::Button,

    #[nwg_control]
    #[nwg_events(OnNotice: [MainApp::update_msg])]
    msg_notice: nwg::Notice,

    #[nwg_control]
    #[nwg_events( OnNotice: [MainApp::read_dialog_output] )]
    dialog_notice: nwg::Notice,

    // 从websocket获取消息
    msg_receiver: RefCell<Option<Receiver<String>>>,
    // 文本输入框输入消息后，发送使用
    msg_sender: RefCell<Option<Sender<String>>>,
    // 退出登录后，关闭websocket线程
    logout_sender: RefCell<Option<Sender<bool>>>,

    is_login: RefCell<bool>,
    user_info: RefCell<UserInfo>,
    dialog_data: RefCell<Option<thread::JoinHandle<String>>>,
}
static EMBEDDED_IMAGE: &[u8] = include_bytes!(".\\..\\images\\getcode.png");
impl MainApp {
    fn load_data(&self) {
        let gimg = &self.img_gzh;
        println!("image1 size is {:?}", gimg.size());
        // let filedata = self.decoder.from_filename("./images/getcode.png").unwrap();
        let filedata = self.decoder.from_stream(EMBEDDED_IMAGE).unwrap();
        let framedata = filedata.frame(0).unwrap();
        println!("image size is {:?}", framedata.size());
        let ndata = self.decoder.resize_image(&framedata, [131, 131]).unwrap();
        let mut img = self.loaded_image.borrow_mut();
        img.replace(ndata.as_bitmap().unwrap());
        gimg.set_bitmap(img.as_ref());
    }

    fn login(&self) {
        let mut is_login = self.is_login.borrow_mut();

        if *is_login {
            *is_login = false;
            let mut sender_ref = self.logout_sender.borrow_mut();
            let sender = sender_ref.as_mut().unwrap();
            sender.send(true).unwrap();
            self.btn_login.set_text("登录");
            self.btn_nickname.set_enabled(false);
            self.btn_send.set_enabled(false);
            self.edt_msg.set_readonly(true);
        } else {
            let vcode = self.edt_code.text();
            if &vcode == "" {
                nwg::modal_error_message(&self.window, "提示", "请输入验证码");
                return;
            }
            self.edt_code.set_text("");
            match do_login(&vcode) {
                Ok(user) => {
                    println!("userinfo is {},{}", user.openid, user.nickname);
                    let nuser = user.clone();
                    *self.user_info.borrow_mut() = nuser;
                    let (sock_sender, sock_receiver) = channel();
                    let (text_sender, text_receiver) = channel();
                    let (logout_sender, logout_receiver) = channel();
                    // Creates a sender to trigger the `OnNotice` event
                    let notice_sender = self.msg_notice.sender();

                    *self.msg_receiver.borrow_mut() = Some(sock_receiver);
                    *self.msg_sender.borrow_mut() = Some(text_sender);
                    *self.logout_sender.borrow_mut() = Some(logout_sender);

                    do_ws_conn(
                        user.openid,
                        notice_sender,
                        sock_sender,
                        text_receiver,
                        logout_receiver,
                    );

                    *is_login = true;
                    self.btn_login.set_text("退出");
                    self.btn_nickname.set_enabled(true);
                    self.btn_send.set_enabled(true);
                    self.edt_msg.set_readonly(false);
                    self.edt_msg.set_focus();
                    // self.cbx_room.set_enabled(true);
                }
                Err(e) => {
                    nwg::modal_error_message(&self.window, "提示", &e.to_string());
                    return;
                }
            }
        }
    }

    fn update_msg(&self) {
        let mut receiver_ref = self.msg_receiver.borrow_mut();
        let receiver = receiver_ref.as_mut().unwrap();
        while let Ok(data) = receiver.try_recv() {
            // let mut new_text = self.msg_box.text();
            // new_text.push_str(&data);
            // self.msg_box.set_text(&new_text);
            // self.msg_box.scroll_lastline();
            self.msg_box.appendln(&data);
            self.msg_box.scroll_lastline();
        }
    }

    // fn switch_room(&self) {
    //     self.msg_box.clear();
    // }

    fn update_nickname(&self) {
        println!("修改名称");
        self.btn_nickname.set_enabled(false);

        *self.dialog_data.borrow_mut() =
            Some(UpdateNickNameDialog::popup(self.dialog_notice.sender()));
    }

    fn read_dialog_output(&self) {
        self.btn_nickname.set_enabled(true);

        let data = self.dialog_data.borrow_mut().take();
        match data {
            Some(handle) => {
                let dialog_result = handle.join().unwrap();
                if dialog_result != "" {
                    self.user_info.borrow_mut().nickname = dialog_result;
                    if let Err(e) = do_update_nickname(
                        &self.user_info.borrow().openid,
                        &self.user_info.borrow().nickname,
                    ) {
                        nwg::modal_error_message(&self.window, "提示", &e.to_string());
                        return;
                    }
                    nwg::modal_info_message(&self.window, "提示", "修改成功");
                }
            }
            None => {}
        }
    }

    fn send_msg(&self) {
        println!("发送消息");
        let msg = self.edt_msg.text();
        // self.msg_box.appendln(&msg);
        // self.edt_msg.set_text("");
        if msg != "" {
            let mut sender_ref = self.msg_sender.borrow_mut();
            let sender = sender_ref.as_mut().unwrap();
            sender.send(msg).unwrap();
            self.edt_msg.set_text("");
        }
    }

    fn exit(&self) {
        nwg::stop_thread_dispatch();
    }
}

fn main() {
    nwg::init().expect("Failed to init Native Windows GUI");
    nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font");

    let _app = MainApp::build_ui(Default::default()).expect("Failed to build UI");

    nwg::dispatch_thread_events();
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct UserInfo {
    pub openid: String,
    pub nickname: String,
}
fn do_login(vcode: &str) -> anyhow::Result<UserInfo> {
    let url = "https://libs.91demo.top/login1109";
    let login_param = [("vcode", vcode)];
    let json_data: serde_json::Value = ureq::post(&url).send_form(&login_param)?.into_json()?;
    let code = &json_data["code"];
    println!("the json data is {}", json_data);
    if code == -1 {
        let err_msg = format!("login error,{}", json_data["msg"]);
        bail!(err_msg)
    }
    let resp_data = &json_data["data"];
    println!("the resp data is {}", resp_data);
    let user_info: UserInfo = serde_json::from_value(resp_data.clone()).unwrap();
    Ok(user_info)
}

fn do_update_nickname(openid: &str, nickname: &str) -> anyhow::Result<()> {
    let url = "https://libs.91demo.top/uptname1111";
    let param = [("openid", openid), ("nickname", nickname)];
    let json_data: serde_json::Value = ureq::post(&url).send_form(&param)?.into_json()?;
    let code = &json_data["code"];
    println!("the json data is {}", json_data);
    if code == -1 {
        let err_msg = format!("login error,{}", json_data["msg"]);
        bail!(err_msg)
    }
    Ok(())
}

fn do_ws_conn(
    openid: String,
    notice_sender: NoticeSender,
    sock_sender: Sender<String>,
    text_receiver: Receiver<String>,
    logout_receiver: Receiver<bool>,
) {
    let ws_url = format!("wss://libs.91demo.top/ws1111?openid={}", openid);
    let url = Url::parse(&ws_url).unwrap();
    let (mut socket, response) = connect(url).expect("Can't connect");

    println!("Connected to the server");
    println!("Response HTTP code: {}", response.status());
    println!("Response contains the following headers:");
    for (ref header, _value) in response.headers() {
        println!("* {}", header);
    }

    // 将read 设置为不阻塞读
    let _ = match socket.get_mut() {
        tungstenite::stream::MaybeTlsStream::Plain(stream) => stream.set_nonblocking(true),
        tungstenite::stream::MaybeTlsStream::Rustls(stream) => {
            stream.get_mut().set_nonblocking(true)
        }
        _ => unimplemented!(),
    };

    thread::spawn(move || loop {
        if let Ok(data) = logout_receiver.try_recv() {
            if data {
                socket.close(None).unwrap();
                break;
            }
        }
        if let Ok(data) = text_receiver.try_recv() {
            socket.send(Message::Text(data)).unwrap();
        }

        let msg_res = socket.read();
        match msg_res {
            Ok(msg) => {
                println!("Received: {}", msg);
                match msg {
                    Message::Text(m) => {
                        println!("receive mesg {:?}", m);
                        sock_sender.send(m).unwrap();
                        notice_sender.notice();
                    }
                    Message::Binary(m) => {
                        println!("receive mesg {:?}", m);
                    }
                    Message::Ping(m) => {
                        println!("receive ping mesg {:?}", m);
                    }
                    Message::Pong(_) => {
                        println!("receive pong message");
                    }
                    Message::Close(m) => {
                        println!("receive mesg {:?}", m);
                        socket.close(None).unwrap();
                        break;
                    }
                    Message::Frame(m) => {
                        println!("receive mesg {:?}", m);
                    }
                }
            }
            Err(e) => println!("get err {}", e),
        }
        // 休息一会儿，防止CPU快速运行
        thread::sleep(Duration::from_millis(100));
    });
}
